package wangjg.test.socks;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

public class Socks5ClientHandler implements Runnable {
	public static final int STATE_CONNECT = 0;
	public static final int STATE_REQUEST = 1;
	public static final int STATE_RECEIVE = 2;

	private int state = STATE_CONNECT;
	
	private byte[] connRepBytes = {5,0};
	
	private Socket client;
	private DataInputStream in;
	private DataOutputStream out;
	
	private Socket target;
	
	private static int n = 1;
	
	public Socks5ClientHandler(Socket socket) {
		System.out.println("client " + n++);
		this.client = socket;
	}
	
	public void run() {
		try {
			in = new DataInputStream(client.getInputStream());
			out = new DataOutputStream(client.getOutputStream());
			while (true) {
				this.receive();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		release();
	}

	public void receive() throws IOException{
		if(state==STATE_CONNECT){
			this.handleConnect();
			this.changeState(STATE_REQUEST);
		}else if(state==STATE_REQUEST){
			this.handleRequest();
			this.changeState(STATE_RECEIVE);
		}else if(state==STATE_RECEIVE){
			//转发
			this.handleReceive();
		}
	}
	
	private void handleConnect() throws IOException{
		byte[] buf=new byte[3];
		int len = in.read(buf,0,3);
		if(buf[0]!=5)
			throw new IOException("not ver5,"+SocksUtil.toHex(buf, 3));
		
		out.write(connRepBytes);
		out.flush();
	}
	
	private void handleRequest() throws IOException{
		int ver = in.read();
		int cmd = in.read();
		int rsv = in.read();
		int atype = in.read();
		String host = null;
		int port = 0;
		if(atype==1){//ipv4 4 bytes
			StringBuilder sb=new StringBuilder();
			host = in.read()+"."+in.read()+"."+in.read()+"."+in.read() ;
		}else if(atype==3){//domain name len+bytes
			int sLen = in.readByte();
			byte[] sbuf=new byte[sLen];
			in.read(sbuf);
			host = new String(sbuf);
		}else if(atype==4){//ipv6 16 bytes
			
		}
		port = in.readShort();
		
		//
		target = new Socket(host, port);
		this.receivTarget();
		//
		byte[] resp = new byte[]{5,0,0,1, 0,0,0,0,0,0};
		out.write(resp);
		out.flush();
	}
	
	private void handleReceive() throws IOException{
//		byte[] buf = new byte[10];
//		int len = in.read(buf);
//		SocksUtil.toHex(buf, len);
		target.getOutputStream().write(in.read());
	}
	
	private void receivTarget() throws IOException{
		final InputStream is = this.target.getInputStream();
		Thread readThread = new Thread(){
			public void run(){
				try {
					byte[] buffer = new byte[100];
					int len=0;
					while((len=is.read(buffer))!=-1){
						out.write(buffer,0,len);
						out.flush();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
				release();
			}
		};
		readThread.start();
	}
	
	private void changeState(int state){
		this.state=state;
	}
	
	private void release(){
		try {
			target.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			client.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
